package cn.ai.shoes.core.config;


import cn.ai.shoes.core.filter.JWTAuthenticationFilter;
import cn.ai.shoes.core.handler.*;
import cn.ai.shoes.core.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.authentication.logout.LogoutFilter;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration// 配置类
@EnableMethodSecurity// 开启方法级别的权限控制
// @EnableWebSecurity//开启是springSecurity自定义配置,(在springboot项目中可以省略)
public class WebSecurityConfig {
    @Resource
    private IgnoreUrlsConfig ignoreUrlsConfig;
    @Resource
    private JWTAuthenticationFilter jwtAuthenticationFilter;
    @Resource
    private UserService userService;
    @Resource
    private DBUserDetailsManager dbUserDetailsManager;


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //开启跨域支持
        http.cors(withDefaults())
                // CSRF 禁用，因为不使用 Session
                .csrf(csrf -> csrf.disable())
                // 基于 token 机制，所以不需要 Session
                .sessionManagement(c -> c.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .headers(c -> c.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
                // 自定义异常处理
                .exceptionHandling(exception -> {
                    exception.authenticationEntryPoint(new MyAuthenticationEntryPoint());
                    exception.accessDeniedHandler(new MyAccessDeniedHandler());
                });


        //打印
        // 其他请求需要认证
        http.authorizeHttpRequests(authorize -> authorize
                // 正确配置多个白名单路径（使用逗号分隔）
                .requestMatchers(ignoreUrlsConfig.getUrls().toArray(new String[0]))
                .permitAll()
                .anyRequest()
                .authenticated()
        );
        // 配置登录和登出
        http.formLogin(form -> form.loginPage("/login")
                .loginProcessingUrl("/login")
                .permitAll()
                .successHandler(new MyAuthenticationSuccessHandler(userService))
                .failureHandler(new MyAuthenticationFailureHandler()));
        http.logout(logout -> {
            logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "POST"));
            logout.logoutSuccessHandler(new MyLogoutSuccessHandler());
        });



        // .httpBasic(Customizer.withDefaults());
        //加入jwt过滤器
        http.addFilterBefore(jwtAuthenticationFilter, LogoutFilter.class);
        http.authenticationProvider(daoAuthenticationProvider(userDetailsService(), passwordEncoder()));
        return http.build();
    }
    @Bean
    public UserDetailsService userDetailsService() {
        return dbUserDetailsManager;
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder);
        provider.setHideUserNotFoundExceptions(false);
        return provider;
    }
}
